package main

import (
	"container/list"
	"fmt"
	"image"
	"image/color"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
	"log/slog"
	"os"
	"path"
)

type Manager struct {
	W, H     int    // 地图大小
	retry    int    // 重试次数
	count    int    // 模拟次数
	score    int    // 得分
	data     string // 模拟数据
	file     string // 模拟数据文件
	enter    bool   // 是否手动输入
	verbose  bool   // 是否显示详细信息
	origin   *Map   // 原始数据
	maxCount int
	maxScore int
	maxPath  *Map
}

func NewManager() *Manager {
	return &Manager{
		W:     10,
		H:     10,
		retry: 1000,
		count: 100,
		score: 5000,
	}
}

func (manager *Manager) init() {
	manager.origin = NewMap(int8(manager.W), int8(manager.H))
	if manager.file != "" {
		if manager.verbose {
			slog.Info("load from file", slog.String("file", manager.file))
		}
		manager.origin.Load(manager.initFile(manager.file))
		return
	}
	if manager.data != "" {
		manager.origin.Load(manager.data)
		return
	}
	manager.origin.Load(nil)
}

func (manager *Manager) initFile(file string) any {
	ext := path.Ext(file)
	switch ext {
	case ".jpg", ".jpeg", ".png", ".gif":
		if manager.verbose {
			slog.Info("match supported image extension", slog.String("ext", ext))
		}
		return manager.initImageFile(file)
	case ".txt":
		if manager.verbose {
			slog.Info("match supported text extension", slog.String("ext", ext))
		}
		return manager.initTxtFile(file)
	default:
		if manager.verbose {
			slog.Error("ignore unsupported extension", slog.String("ext", ext))
		}
		return nil
	}
}

func (manager *Manager) initTxtFile(file string) any {
	if b, err := os.ReadFile(file); err != nil {
		if manager.verbose {
			slog.Error("read text file failed", slog.String("file", file), slog.String("err", err.Error()))
		}
		return nil
	} else {
		if manager.verbose {
			slog.Info("read text file success", slog.String("file", file))
		}
		return b
	}
}

func (manager *Manager) initImageFile(file string) any {
	fp, err := os.Open(file)
	if err != nil {
		if manager.verbose {
			slog.Error("open image file failed", slog.String("file", file), slog.String("err", err.Error()))
		}
	}
	defer fp.Close()
	img, _, err := image.Decode(fp)
	if err != nil {
		if manager.verbose {
			slog.Error("decode image file failed", slog.String("file", file), slog.String("err", err.Error()))
		}
		panic(err)
	}
	if manager.verbose {
		slog.Info("decode image file success", slog.String("file", file))
		slog.Info("image size", slog.Int("width", img.Bounds().Dx()), slog.Int("height", img.Bounds().Dy()))
	}
	px := img.Bounds().Dx() / manager.W
	py := img.Bounds().Dy() / manager.H
	cx, cy := px/4, py/4 // 中心点有个亮光避免影响颜色对比
	ret := make([]color.Color, 0, manager.W*manager.H)
	for i := range manager.H {
		for j := range manager.W {
			ret = append(ret, img.At(j*px+cx, i*py+cy))
		}
	}
	return ret
}

func (manager *Manager) Run() {
	manager.init()
	for k := range manager.retry {
		if manager.maxScore > manager.score {
			break
		}
		if manager.verbose {
			slog.Info("try other path", slog.Int("retry", k), slog.Int("max score", manager.maxScore))
		}
		manager.maxCount = 0
		manager.try(manager.origin)
	}
}

func (manager *Manager) try(m *Map) {
	if manager.maxCount > manager.count || manager.maxScore > manager.score {
		return
	}
	m.GenerateChildrens()
	if len(m.childrens) == 0 {
		manager.maxCount++
		score := int(m.TotalScore())
		if manager.maxScore < score {
			if manager.verbose {
				slog.Info("current max score", slog.Int("score", score))
			}
			manager.maxScore = score
			manager.maxPath = m
		}
		return
	}
	for _, child := range m.childrens {
		manager.try(child)
		m.RemoveFirstChildren()
	}
}

func (manager *Manager) Show() {
	stack := list.New()
	m := manager.maxPath
	for m != nil {
		stack.PushFront(m)
		m = m.Parent()
	}
	m = manager.origin
	fmt.Println("原始数据")
	m.Dump()
	stack.Remove(stack.Front())
	for e := stack.Front(); e != nil; e = e.Next() {
		m = e.Value.(*Map)
		fmt.Printf("消除(%d): +%d = %d\n", m.ClearCount(), m.ClearScore(), m.Score())
		m.Dump()
		if manager.enter {
			fmt.Scanln()
		}
	}
	fmt.Printf("剩余(%d): +%d = %d\n", m.RemainCount(), m.RemainScore(), m.TotalScore())
}
